package cn.edu.cug.cs.gtl.gsm.disk;

import cn.edu.cug.cs.gtl.common.Identifier;
import cn.edu.cug.cs.gtl.gsm.PaginationStorageManager;

import java.io.IOException;
import java.io.RandomAccessFile;

/**
 * 实现本地磁盘多文件分页读写
 */
public class DiskStorageManager extends PaginationStorageManager {
    //当前数据文件，按照页面进行读写
    RandomAccessFile dataFile;

    /**
     * 索引文件结构如下：
     * 1-8：pageSize int
     * 9-16：nextPage int
     * 17-20:emptyPagesCount 空页面的数目 int
     * 21-EPC: 记录空页面的ID EPC=(13+emptyPagesCount*4)
     * EPC-(EPC+4): pageIndex中数据项数 PIC
     * IndexItem 的第0个页面ID int
     * IndexItem的数据长度  int
     * IndexItem中的页面总数 int
     * IndexItem中的每个页面的ID，包括第0个
     */
    RandomAccessFile indexFile;


    /**
     * @param baseName  文件名
     * @param collectionSize 每个文件中最大页面个数
     * @param pageSize  指定页面存储大小
     * @param overWrite 是否覆盖原文件
     * @throws IOException
     */
    public DiskStorageManager(String baseName, long collectionSize,int pageSize, boolean overWrite) throws Exception {
        super(collectionSize,pageSize);
        this.metaItem.setBaseName(baseName);
        String dataFileName = baseName + "_0.dat";
        String indexFileName = baseName + ".idx";
        long length = 0; // 记录文件长度

        //如果文件存在则以读写方式打开，如果不存在则创建文件并以读写方式打开
        this.dataFile = new RandomAccessFile(dataFileName, "rw");
        this.indexFile = new RandomAccessFile(indexFileName, "rw");

        if (overWrite) {
            //如果要覆盖原有数据，将文件长度设置为0
            this.indexFile.setLength(0L);
            this.dataFile.setLength(0L);
        }
        // get current length of file
        length = this.indexFile.length();

        //find page size
        if (length == 0 || overWrite == true) {//如果索引文件长度为0
            //this.pageSize = pageSize;
            this.getNextPageIdentifier().reset(0);
        } else {
            //从索引文件中读取MetaItem
            readMetaFromStorage();
        }
        //如果已经存在不为空的索引信息
        if ((overWrite == false) && (length > 0)) {
            readIndicesFromStorage();
        }
    }

    @Override
    protected void readBufferFromPage(Identifier pageIdentifier) throws IOException {
        int order = this.metaItem.getCollectionOrder(pageIdentifier);
        long pos = pageIdentifier.longValue()*getPageSize() - getCollectionSize()*order*getPageSize();
        //需要进行文件切换
        if(order!=getCurrentCollectionOrder()){
            this.dataFile.close();
            setCurrentCollectionOrder(order);
            String dataFileName = this.metaItem.getBaseName() + "_"+String.valueOf(getCurrentCollectionOrder()).toString()+".dat";
            //如果文件存在则以读写方式打开，如果不存在则创建文件并以读写方式打开
            this.dataFile = new RandomAccessFile(dataFileName, "rw");
        }
        this.dataFile.seek(pos);
        this.dataFile.read(this.pageBuffer);

    }

    @Override
    protected void writeBufferToPage(Identifier pageIdentifier) throws IOException {
        int order = this.metaItem.getCollectionOrder(pageIdentifier);
        long pos = pageIdentifier.longValue()*getPageSize() - getCollectionSize()*order*getPageSize();
        //需要进行文件切换
        if(order!=getCurrentCollectionOrder()){
            this.dataFile.close();
            setCurrentCollectionOrder(order);
            String dataFileName = this.metaItem.getBaseName() + "_"+String.valueOf(getCurrentCollectionOrder()).toString()+".dat";
            //如果文件存在则以读写方式打开，如果不存在则创建文件并以读写方式打开
            this.dataFile = new RandomAccessFile(dataFileName, "rw");
        }
        this.dataFile.seek(pos);
        this.dataFile.write(this.pageBuffer);
    }

    @Override
    protected void readMetaFromStorage() throws IOException {
        this.indexFile.seek(0);
        int c = this.indexFile.readInt();
        if(c<=0) return;
        byte[] bs = new byte[c];
        this.indexFile.read(bs);
        this.metaItem.loadFromByteArray(bs);
    }

    @Override
    protected void writeMetaToStorage() throws IOException {
        this.indexFile.seek(0);
        byte[] bs = this.metaItem.storeToByteArray();
        this.indexFile.writeInt(bs.length);
        this.indexFile.write(bs);
    }

    @Override
    protected void readIndicesFromStorage() throws IOException {
        int c = this.indexFile.readInt();
        if(c<=0) return;
        byte[] bs = new byte[c];
        this.indexFile.read(bs);
        this.pageMap.loadFromByteArray(bs);
    }

    @Override
    protected void writeIndicesToStorage() throws IOException {
        byte[] bs = this.pageMap.storeToByteArray();
        this.indexFile.writeInt(bs.length);
        this.indexFile.write(bs);
    }

    @Override
    protected void closeStorages() throws IOException {
        this.dataFile.close();
        this.indexFile.close();
    }
}
